package com.distribute.application.api;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.Arrays;

import org.reflections.Reflections;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;

import com.alibaba.fastjson.JSON;
import com.distribute.framework.core.lang.*;
import com.distribute.framework.core.web.helper.ParamHelper;
import com.distribute.framework.core.web.helper.Utils;
import com.distribute.framework.model.*;
public class ApiHelper {
	
	/**
	 * 检验参数签名
	 * @param secret
	 * @param sign 客方传过来的加密串，格式为secret+(参数名+参数值...)+secret的大写md5值
	 * @param paramNames
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static Boolean verifySign(Map params,String secret,String sign,String... paramNames){
		//按参数名asscic码排序
		List<String> names=new ArrayList();
		for(String paramName:paramNames)
			names.add(paramName);
		java.util.Collections.sort(names);
		String strSign = secret;
		for(String name:names){
			strSign+=name+ParamHelper.getString(params, name, "");			
		}
		strSign+=secret;
		String md5 = Utils.md5(strSign).toUpperCase();
		return Utils.equals(md5,sign);
	}
	
	
	/**
	 * 获取加密码串
	 * @param secret
	 * @param params
	 * @return
	 */
	public static String sign(String secret,Map<String,String> params){
		//按参数名asscic码排序
		List<String> names=new ArrayList();
		names.addAll(params.keySet());
		java.util.Collections.sort(names);
		String strSign = secret;
		for(String key:names)
			strSign+=key+params.get(key);
		strSign+=secret;
		return Utils.md5(strSign).toUpperCase();
	}
	
	/**
	 * 简单化json输出
	 * @param json
	 * @param prefix 字段要去掉的前缀
	 * @return
	 */
	public static Map simple(Map<String,Object> json,String prefix){
		Map result = new java.util.HashMap();
		for(String key:json.keySet()){
			if(key==null)continue;
			result.put(Utils.forMat(key.replace(prefix, "")), json.get(key));
		}
		return result;
	}
	
		
	static Map<String,List<Map>> method_mapping = new HashMap();
	public static List<Map> getParamInfos(Class scriptClass,String methodName) throws Exception{
		if(method_mapping.get(scriptClass.getName()+"."+methodName)!=null){
			return method_mapping.get(scriptClass.getName()+"."+methodName);
		}
		ClassPool pool = ClassPool.getDefault();
        ClassClassPath classPath = new ClassClassPath(scriptClass);
        pool.insertClassPath(classPath);
        CtClass ctClass = pool.get(scriptClass.getName());	
        
        
        Method method = null;
        for(Method m:scriptClass.getDeclaredMethods()){
        	if(m.getAnnotation(RequestMapping.class)!=null && m.getName().equals(methodName)){
        		method = m;
        	}
        }
        Class<?>[] parameterTypes = method.getParameterTypes();
		Annotation[][] parameterAnnotations = method.getParameterAnnotations();
		CtMethod ctMethod = ctClass.getDeclaredMethod(methodName);
        MethodInfo methodInfo = ctMethod.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        
        String[] parameterNames = new String[parameterTypes.length];
      //省略前面代码部分
        TreeMap<Integer, String> sortMap = new TreeMap<Integer, String>();
        for (int i = 0; i < attr.tableLength(); i++) 
            sortMap.put(attr.index(i), attr.variableName(i));
        int pos = Modifier.isStatic(ctMethod.getModifiers()) ? 0 : 1;
        parameterNames = Arrays.copyOfRange(sortMap.values().toArray(new String[0]), pos, parameterNames.length + pos);
        
        List paramList = new ArrayList();
        for (int i=0;i<ctMethod.getParameterTypes().length;i++){
        	Annotation[] paramAnnotations = parameterAnnotations[i];
        	title paramTitle = null;
        	for(Annotation annotation:paramAnnotations){
        		if(annotation.annotationType()==title.class){
        			paramTitle = (title) annotation;
        		}
        	}
            String paramName = parameterNames[i];
            String paramType = parameterTypes[i].getSimpleName();		            
            Map paramMap = new HashMap();
            paramMap.put("name", paramName);
            paramMap.put("notnull", ""+(paramTitle.value().contains("*")));
            paramMap.put("title", paramTitle.value().replace("*", ""));
            paramMap.put("type", paramType);
            paramList.add(paramMap);
        }
        method_mapping.put(scriptClass.getName()+"."+methodName,paramList);
		return paramList;
	}
	
	/**
	 * 根据类保存API
	 * @param cls
	 */
	public static void syncApiInfo(Class scriptClass){		
		try{
			ClassPool pool = ClassPool.getDefault();
	        ClassClassPath classPath = new ClassClassPath(scriptClass);
	        pool.insertClassPath(classPath);
	        CtClass ctClass = pool.get(scriptClass.getName());	        
			for(Method method:scriptClass.getDeclaredMethods()){
				if(method.getAnnotation(RequestMapping.class)==null)continue;
				if(method.getAnnotation(title.class)==null)continue;
				title apiTitle = method.getAnnotation(title.class);
				RequestMapping apiFlag = method.getAnnotation(RequestMapping.class);
				Class<?>[] parameterTypes = method.getParameterTypes();
				Annotation[][] parameterAnnotations = method.getParameterAnnotations();
				CtMethod ctMethod = ctClass.getDeclaredMethod(method.getName());
		        MethodInfo methodInfo = ctMethod.getMethodInfo();
		        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
		        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
		        int pos = Modifier.isStatic(ctMethod.getModifiers()) ? 0 : 1;
		        List paramList = new ArrayList();
		        for (int i=0;i<ctMethod.getParameterTypes().length;i++){
		        	Annotation[] paramAnnotations = parameterAnnotations[i];
		        	title paramTitle = null;
		        	for(Annotation annotation:paramAnnotations){
		        		if(annotation.annotationType()==title.class){
		        			paramTitle = (title) annotation;
		        		}
		        	}
		            String paramName = attr.variableName(i + pos);
		            String paramType = parameterTypes[i].getSimpleName();		            
		            Map paramMap = new HashMap();
		            paramMap.put("name", paramName);
		            paramMap.put("title", paramTitle==null?"null":paramTitle.value().replace("*", ""));
		            paramMap.put("notnull", paramTitle==null ? false : (""+(paramTitle.value().contains("*"))));
		            paramMap.put("type", paramType);
		            paramList.add(paramMap);
		        }
		        List returnList = (List) JSON.parse("[{\"name\":\"success\",\"title\":\"是否成功\",\"type\":\"Boolean\",\"define\":[],\"field\":\"\"},{\"name\":\"message\",\"title\":\"错误信息\",\"type\":\"String\",\"define\":[],\"field\":\"\"},{\"name\":\"error\",\"title\":\"错误代码\",\"type\":\"String\",\"define\":[],\"field\":\"\"},{\"name\":\"attach\",\"title\":\"附加内容\",\"type\":\"String\",\"define\":[],\"field\":\"\"},{\"name\":\"data\",\"title\":\"数据部分\",\"type\":\"Map\",\"define\":[],\"field\":\"\"}]");
		        List dataList = (List) ((Map)returnList.get(returnList.size()-1)).get("define");
		        
		        if(SrvApiType.where.satId(1).get()==null){
		        	new SrvApiType(
	        		 	"默认分类"//API名称 [非空]
		        		,0l//所属分类 [非空]
	        		 	,"default"//分类标识
	        		 	,new Date()//修改时间 [非空]
	        		 	,new Date()//添加时间 [非空]
	        		 	,(short)1//状态 [非空]
	        		 ).save();
		        }
		        
		        SrvApi srvApi = SrvApi.where.saNamespace(apiFlag.value()[0]).get();
		        		        
		        if(srvApi!=null){
		        	srvApi.setSaParams(JSON.toJSONString(paramList));
		        	if(Utils.empty(srvApi.getSaReturnStruct()))
		        		srvApi.setSaReturnStruct(JSON.toJSONString(returnList));
		        	srvApi.update();
		        }else{
		        	new SrvApi(
	        			apiTitle.value().replace("*", "")//API名称 [非空]
		        	 	,apiFlag.value()[0]//名称空间 [非空]
		        	 	,1l//接口分类 [非空]
		        	 	,Utils.getShort("saVersion")//版本号 [非空]
		        	 	,Utils.getShort("saOpen")//是否开放 [非空]
		        	 	,(short)(apiTitle.value().contains("*")?1:0)//需要用户授权 [非空]
		        	 	,Utils.getString("saDescript")//接口说明
		        	 	,Utils.getString("saClass")//接口类名 [非空]
		        	 	,Utils.getShort("saDir")//数据方向 [非空]
		        	 	,Utils.getString("saMethod")//函数名称 [非空]
		        	 	,(short)0//记录日志
		        	 	,JSON.toJSONString(paramList)//参数结构
		        	 	,JSON.toJSONString(returnList)//返回结构 [非空]
		        	 	,null//错误代码
		        	 	,new Date()//修改时间 [非空]
		        	 	,new Date()//添加时间 [非空]
		        	 	,(short)1//状态 [非空]
		        	 ).save();
		        }
			}
		}catch(Exception ex){
			ex.printStackTrace();
		}
	}
	
	/**
	 * 根据包名保存下面的所有API信息
	 * @param packageName
	 */
    public static void syncPackage(String packageName) {
    	Reflections reflections = new Reflections(packageName);
        Set<Class<?>> classes =  reflections.getTypesAnnotatedWith(RestController.class);
        for (Class cls : classes) {
        	syncApiInfo(cls);
        }
    }
	
	public static void main(String...strings){
		//Utils.trace(struct(SrvUser.class));
	}

}